// 线段树 --- 懒标记
// 区间修改 + 区间查询
#include <iostream>
using namespace std;
typedef long long ll;
#define lc p << 1
#define rc p << 1 | 1
const int N = 1e5 + 10;
int a[N];

struct node
{
    int l, r;
    int s1, m1; // 1的个数, 连续1的最大个数(分治)
    int l1, r1; // 左端点开始的连续1的最长长度, 右端点开始的连续1的最长长度
    int f, rev; // 懒标记, 重置/翻转
    // 由于要执行翻转任务, 翻转之后无法直接求连续1的最大个数, 因此还需要维护0相关信息
    int s0, m0, l0, r0;
} tr[N << 2];

void pushup(node &p, node &l, node &r)
{
    // 1:
    p.s1 = l.s1 + r.s1;
    p.l1 = l.s0 == 0 ? l.s1 + r.l1 : l.l1;
    p.r1 = r.s0 == 0 ? r.s1 + l.r1 : r.r1;
    p.m1 = max(l.m1, max(r.m1, l.r1 + r.l1));
    // 0:
    p.s0 = l.s0 + r.s0;
    p.l0 = l.s1 == 0 ? l.s0 + r.l0 : l.l0;
    p.r0 = r.s1 == 0 ? r.s0 + l.r0 : r.r0;
    p.m0 = max(l.m0, max(r.m0, l.r0 + r.l0));
}

void build(int p, int l, int r)
{
    tr[p] = {l, r, a[l], a[l], a[l], a[l], -1, -1, a[l] ^ 1, a[l] ^ 1, a[l] ^ 1, a[l] ^ 1};
    if (l == r)
        return;
    int mid = (l + r) >> 1;
    build(lc, l, mid);
    build(rc, mid + 1, r);
    pushup(tr[p], tr[lc], tr[rc]);
}

void lazy(int p, int op)
{
    if (op == 0) // 变0
    {
        tr[p].s1 = tr[p].m1 = tr[p].l1 = tr[p].r1 = 0;
        tr[p].s0 = tr[p].m0 = tr[p].l0 = tr[p].r0 = tr[p].r - tr[p].l + 1;
        tr[p].f = 0;
        tr[p].rev = -1;
    }
    if (op == 1) // 变1
    {
        tr[p].s0 = tr[p].m0 = tr[p].l0 = tr[p].r0 = 0;
        tr[p].s1 = tr[p].m1 = tr[p].l1 = tr[p].r1 = tr[p].r - tr[p].l + 1;
        tr[p].f = 1;
        tr[p].rev = -1;
    }
    if(op == 2) // 翻转
    {
        swap(tr[p].s0, tr[p].s1);
        swap(tr[p].m0, tr[p].m1);
        swap(tr[p].l0, tr[p].l1);
        swap(tr[p].r0, tr[p].r1);
        tr[p].rev = tr[p].rev == 2 ? -1 : 2;
    }
}

void pushdown(int p)
{
    // 先执行重置任务
    lazy(lc, tr[p].f);
    lazy(rc, tr[p].f);
    // 再执行翻转任务
    lazy(lc, tr[p].rev);
    lazy(rc, tr[p].rev);
    tr[p].f = tr[p].rev = -1;
}

void modify(int p, int x, int y, int op)
{
    int l = tr[p].l, r = tr[p].r;
    if (x <= l && r <= y)
    {
        lazy(p, op);
        return;
    }
    pushdown(p);
    int mid = (l + r) >> 1;
    if (x <= mid)
        modify(lc, x, y, op);
    if (y > mid)
        modify(rc, x, y, op);
    pushup(tr[p], tr[lc], tr[rc]);
}

node query(int p, int x, int y)
{
    int l = tr[p].l, r = tr[p].r;
    if (x <= l && r <= y)
        return tr[p];
    pushdown(p);
    int mid = (l + r) >> 1;
    if (y <= mid)
        return query(lc, x, y);
    if (x > mid)
        return query(rc, x, y);
    node ret, L = query(lc, x, y), R = query(rc, x, y);
    pushup(ret, L, R);
    return ret;
}

int main()
{
    int n, m;
    cin >> n >> m;
    for (int i = 1; i <= n; i++)
        cin >> a[i];
    build(1, 1, n);
    while (m--)
    {
        int op, x, y;
        cin >> op >> x >> y;
        x++, y++;
        if (op < 3)
            modify(1, x, y, op);
        else if (op == 3)
            cout << query(1, x, y).s1 << endl;
        else
            cout << query(1, x, y).m1 << endl;
    }
    return 0;
}
